﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using RikaScript.Exception;
using RikaScript.Logger;

namespace RikaScript
{
    /// <summary>
    /// RikaScript 引擎，支持 if while func 等语法
    /// </summary>
    public class Engine
    {
        /// <summary>
        /// 所用运行时
        /// </summary>
        public readonly Runtime Runtime;

        /// <summary>
        /// 过程列表，存放的是过程名和过程代码
        /// </summary>
        public readonly Dictionary<string, Function> Functions = new Dictionary<string, Function>();

        /// <summary>
        /// 最后一次执行的代码，显示报错的时候用得到
        /// </summary>
        private string _lastCode = "";

        /// <summary>
        /// 当前构建的过程名
        /// </summary>
        private string _currentBuildingFuncName = "";

        /// <summary>
        /// 当前构建的过程用途，可以有func、
        /// </summary>
        private Function.FunctionType _currentBuildingFuncType;

        /// <summary>
        /// 当前构建的过程参数
        /// </summary>
        private string[] _currentBuildingFuncArgs = new string[0];

        /// <summary>
        /// 当前构建的过程的条件，给 if 和 while 用的
        /// </summary>
        private string _currentBuildingFuncRequirement = "";

        /// <summary>
        /// 过程构建器
        /// </summary>
        private readonly StringBuilder _functionBuilder = new StringBuilder();

        /// <summary>
        /// 当前{的深度
        /// </summary>
        private int _currentDepth = 0;

        /// <summary>
        /// 做随机数用的
        /// </summary>
        private readonly Random _random = new Random((int) DateTime.Now.Ticks);

        /// <summary>
        /// 长度为 0 的 string 数组，经常会用到，为了避免多次 new 就在这里存一份吧
        /// </summary>
        private readonly string[] _zeroLengthStringArray = new string[0];

        public Engine(LoggerBase logger)
        {
            // 实例化 runtime 的时候把自己送过去
            Runtime = new Runtime(logger, this);
        }

        /// <summary>
        /// 开始构建一个代码块
        /// </summary>
        /// <param name="name">代码块名</param>
        /// <param name="expression">表达式，给if和while用的</param>
        /// <param name="type">代码块类型</param>
        /// <param name="args">参数列表</param>
        private void StartBuildFunction(string name, string expression, Function.FunctionType type, string[] args)
        {
            // 深度检查
            if (_currentDepth != 0)
            {
                throw new RuntimeException("不太对劲吧，当前构建深度不是 0 ，不能构建新的代码块");
            }

            _currentBuildingFuncName = name;
            _currentBuildingFuncRequirement = expression;
            _currentBuildingFuncType = type;
            _currentBuildingFuncArgs = args ?? _zeroLengthStringArray;
            // _funcBuilder = new StringBuilder();
            _functionBuilder.Remove(0, _functionBuilder.Length);
            _currentDepth++;
        }

        /// <summary>
        /// 【核心代码】执行一段代码，返回最后一句代码的执行结果
        /// </summary>
        public object Execute(string code)
        {
            object result = null; // 这是最后一次执行代码产生的结果
            var codeLines = code.Trim('\n', '\r', ' ').Split('\n');
            var next = true;
            foreach (var currentLineCode in codeLines)
            {
                if (!next) return result;
                try
                {
                    // 预处理
                    var codeTrim = currentLineCode.Trim();
                    _lastCode = codeTrim;
                    // 跳过代码用的
                    if (codeTrim.Length == 0 || codeTrim.StartsWith("//")) continue;

                    // 如果当前不在构建过程当中，则执行各种命令
                    if (_currentBuildingFuncName.Length == 0)
                    {
                        // 根据是否有空格来判断是否是一个值得解析的语句，如 if set while 这种，否则直接执行原代码
                        if (codeTrim.Contains(" "))
                        {
                            var starts = codeTrim.Substring(0, codeTrim.IndexOf(" ", StringComparison.Ordinal));
                            switch (starts)
                            {
                                case "var":
                                {
                                    // 定义变量
                                    var match = Regex.Match(codeTrim, @"^var +([^ ]+) *(=?) *(.*)$");
                                    if (!match.Success)
                                        throw new EngineException("var 格式错误");
                                    // 分别获取变量名、赋值号或值
                                    var varName = match.Groups[1].Value;
                                    var op = match.Groups[2].Value;
                                    var value = match.Groups[3].Value;

                                    if (op == "=")
                                    {
                                        // 创建变量并赋值
                                        Runtime.Execute(":=('" + varName + "'," + value + ")");
                                    }
                                    else
                                    {
                                        // 创建空变量
                                        Runtime.Execute(":=('" + varName + "',null)");
                                    }

                                    break;
                                }
                                case "set":
                                {
                                    // 给变量赋值
                                    var m = Regex.Match(codeTrim, @"^set +([^ ]+) *(.*?=) *(.+)$");
                                    if (!m.Success)
                                        throw new EngineException("set 格式错误");

                                    // 变量名、操作符、值
                                    var varName = m.Groups[1].Value;
                                    var op = m.Groups[2].Value;
                                    var value = m.Groups[3].Value;
                                    // 赋值操作
                                    if (op == "=")
                                    {
                                        result = Runtime.Execute("'" + varName + "' " + op + " " + value);
                                    }
                                    // 相对赋值操作
                                    else if (op.EndsWith("="))
                                    {
                                        var command =
                                            "'{0}' = {1}({0}, {2})".Format(varName, op.Substring(0, op.Length - 1),
                                                value);
                                        result = Runtime.Execute(command);
                                    }

                                    break;
                                }
                                case "if":
                                {
                                    // if 语句
                                    var match = Regex.Match(codeTrim, @"^if +(.+)( *({)| +(.+))$");
                                    if (!match.Success)
                                        throw new EngineException("if 格式错误");

                                    var group = match.Groups;
                                    // if 表达式和方法名
                                    var expression = group[1].Value;
                                    var funcName = group[2].Value.Trim();

                                    if (funcName == "{")
                                    {
                                        // 如果方法名是 { 则开始构建子代码块
                                        StartBuildFunction(
                                            "_IF_FUNCTION_" + _random.Next(),
                                            expression,
                                            Function.FunctionType.IF,
                                            _zeroLengthStringArray
                                        );
                                    }
                                    else
                                    {
                                        // 如果方法名是个方法，则根据条件调用方法
                                        var value = Runtime.Execute(expression).Bool();
                                        if (!value) continue;
                                        if (funcName == "return")
                                        {
                                            next = false;
                                            break;
                                        }

                                        this.Execute("call " + funcName);
                                    }

                                    break;
                                }
                                case "while":
                                {
                                    // while
                                    var match = Regex.Match(codeTrim, @"^while +(.+)( *({)| +(.+))$");
                                    if (!match.Success)
                                        throw new EngineException("while 格式错误");

                                    var group = match.Groups;
                                    // 表达式、方法名
                                    var expression = group[1].Value;
                                    var funcName = group[2].Value;

                                    if (funcName == "{")
                                    {
                                        // 开始构建方法
                                        StartBuildFunction(
                                            "_WHILE_FUNCTION_" + _random.Next(),
                                            expression,
                                            Function.FunctionType.WHILE,
                                            _zeroLengthStringArray
                                        );
                                    }
                                    else
                                    {
                                        while (Runtime.Execute(expression).Bool())
                                        {
                                            this.Execute("call " + funcName);
                                        }
                                    }

                                    break;
                                }
                                case "func":
                                {
                                    // 定义过程
                                    var m = Regex.Match(codeTrim, @"^func +(.+?)(\((.*?)\))? *{$");
                                    if (!m.Success)
                                        throw new EngineException("func 格式错误");

                                    var funcName = m.Groups[1].ToString();

                                    // 过程参数列表
                                    var argsStr = m.Groups[3].ToString().Replace(" ", "");
                                    var args = argsStr.Split(',');
                                    if (argsStr.Length == 0)
                                    {
                                        args = _zeroLengthStringArray;
                                    }

                                    // 开始构建函数
                                    StartBuildFunction(funcName, "", Function.FunctionType.FUNCTION, args);
                                    break;
                                }
                                case "call":
                                {
                                    var match = Regex.Match(codeTrim, @"^call +(.+?)$");
                                    if (!match.Success)
                                        throw new EngineException("call 格式错误");
                                    var funcName = match.Groups[1].ToString();

                                    if (!Functions.ContainsKey(funcName + "^" + 0))
                                        throw new NotFoundFuncException(funcName);

                                    var funcCode = Functions[funcName + "^" + 0];

                                    Runtime.Execute(":ENTER:()");
                                    result = this.Execute(funcCode.Body);
                                    Runtime.Execute(":EXIT:()");
                                    break;
                                }
                                case "exec":
                                {
                                    var match = Regex.Match(codeTrim, "^exec +(.+)$");
                                    if (!match.Success)
                                        throw new EngineException("exec 格式错误");
                                    var group = match.Groups;
                                    var allText = File.ReadAllText(group[1].Value);
                                    this.Execute(allText);
                                    break;
                                }
                                default:
                                {
                                    result = Runtime.Execute(codeTrim);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            result = Runtime.Execute(codeTrim);
                        }
                    }
                    // 如果正在定义过程，则把全部内容添加到过程内容里
                    else
                    {
                        // 新代码段，深度 + 1
                        if (codeTrim.EndsWith("{"))
                        {
                            _currentDepth++;
                        }

                        // 结束代码段，深度 - 1
                        if (codeTrim == "}")
                        {
                            _currentDepth--;
                            // 如果代码段全结束了！
                            if (_currentDepth == 0)
                            {
                                var name = _currentBuildingFuncName.Trim();
                                var type = _currentBuildingFuncType;
                                var args = _currentBuildingFuncArgs;
                                var requirement = _currentBuildingFuncRequirement;

                                _currentBuildingFuncName = "";
                                _currentBuildingFuncType = Function.FunctionType.FUNCTION;
                                _currentBuildingFuncArgs = _zeroLengthStringArray;
                                _currentBuildingFuncRequirement = "";

                                var body = _functionBuilder.ToString().Trim('\n', ' ');

                                Functions[name + "^" + args.Length] = new Function(body, args);

                                switch (type)
                                {
                                    case Function.FunctionType.FUNCTION:
                                        break;
                                    case Function.FunctionType.IF:
                                        this.Execute("if " + requirement + " " + name);
                                        Functions.Remove(name);
                                        break;
                                    case Function.FunctionType.WHILE:
                                        this.Execute("while " + requirement + " " + name);
                                        Functions.Remove(name);
                                        break;
                                }
                            }
                            else
                            {
                                _functionBuilder.Append(codeTrim + "\n");
                            }
                        }
                        else
                        {
                            _functionBuilder.Append(codeTrim + "\n");
                        }
                    }
                }
                catch (System.Exception e)
                {
                    Runtime.Logger.ShowException(e, _lastCode);
                    break;
                }
            }

            return result;
        }

        /// <summary>
        /// 代表一个过程的类
        /// </summary>
        public class Function
        {
            public string Body { get; set; }
            public string[] Arguments { get; set; }

            public Function(string body, string[] arguments)
            {
                this.Body = body;
                this.Arguments = arguments ?? new string[0];
            }

            public enum FunctionType
            {
                IF,
                WHILE,
                CODES,
                FUNCTION,
                IGNORE,
            }
        }
    }
}